package com.wushijia.java8.concurrent;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author Benjamin Winterberg
 */
public class Executors3 {

  public static void main(String[] args) throws InterruptedException, ExecutionException {
//    test1();
//        test2();
//        test3();

//        test4();
//        test5();
    test06();
  }

  private static void test5() throws InterruptedException, ExecutionException {
    ExecutorService executor = Executors.newWorkStealingPool();

    List<Callable<String>> callables = Arrays.asList(
        callable("task1", 2),
        callable("task2", 1),
        callable("task3", 3));

    String result = executor.invokeAny(callables);
    System.out.println(result);

    executor.shutdown();
  }

  private static Callable<String> callable(String result, long sleepSeconds) {
    return () -> {
      TimeUnit.SECONDS.sleep(sleepSeconds);
      return result;
    };
  }

  private static void test4() throws InterruptedException {
    ExecutorService executor = Executors.newWorkStealingPool();

    List<Callable<String>> callables = Arrays.asList(
        () -> "task1",
        () -> "task2",
        () -> "task3");

    executor.invokeAll(callables)
        .stream()
        .map(future -> {
          try {
            return future.get();
          } catch (Exception e) {
            throw new IllegalStateException(e);
          }
        })
        .forEach(System.out::println);

    executor.shutdown();
  }

  private static void test3() {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    Runnable task = () -> {
      try {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("Scheduling: " + System.nanoTime());
      } catch (InterruptedException e) {
        System.err.println("task interrupted");
      }
    };

    executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS);
  }

  private static void test2() {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime());
    int initialDelay = 0;
    int period = 1;
    //周期性执行
    executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);
  }

  private static void test1() throws InterruptedException {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

    Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime());
    int delay = 3;
    ScheduledFuture<?> future = executor.schedule(task, delay, TimeUnit.SECONDS);

    TimeUnit.MILLISECONDS.sleep(1337);

    long remainingDelay = future.getDelay(TimeUnit.MILLISECONDS);
    System.out.printf("Remaining Delay: %sms\n", remainingDelay);
  }

  private static void test06() throws InterruptedException {
    // 创建大小为5的线程池
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    for (int i = 0; i < 3; i++) {
      Task worker = new Task("task-" + i);
      // 只执行一次
//          scheduledThreadPool.schedule(worker, 5, TimeUnit.SECONDS);
      // 周期性执行，每5秒执行一次
      scheduledThreadPool.scheduleAtFixedRate(worker, 0, 5, TimeUnit.SECONDS);
    }
    Thread.sleep(10000);
    System.out.println("Shutting down executor...");
    // 关闭线程池
    scheduledThreadPool.shutdown();
    boolean isDone;
    // 等待线程池终止
    do {
      isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.DAYS);
      System.out.println("awaitTermination...");
    } while (!isDone);
    System.out.println("Finished all threads");
  }
}

class Task implements Runnable {

  private String name;

  public Task(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    System.out.println("name = " + name + ", startTime = " + new Date());
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("name = " + name + ", endTime = " + new Date());
  }
}
